using System.Security.Claims;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using PowerManage.API.AuditLog;
using PowerManage.Entity;
using PowerManage.Entity.Dtos.Accounts;
using PowerManage.Entity.Enums;
using PowerManage.Service;

namespace PowerManage.API.Auth;

/// <summary>
/// 基于角色的自定义授权：API权限验证
/// </summary>
public class PermissionAuthorizeAttribute : AuthorizeAttribute, IAsyncAuthorizationFilter
{
    /// <summary>
    /// 用于执行具体的权限检查逻辑
    /// </summary>
    /// <param name="context">授权过滤器上下文，包含有关HTTP请求的信息以及请求的服务和用户身份。</param>
    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        // 1、先根据登录的用户，获取权限列表；
        var userName = context.HttpContext.User.Identity.Name;
        var role = context.HttpContext.User.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value)
            .FirstOrDefault();
        //依赖注入： 这里使用请求范围内的服务注入：每次请求时获取最新状态
        // 使用 typeof 获取类型对象，然后通过 GetService 方法获取相应类型的实例。 
        // 最后使用 as 关键字进行类型转换，确保类型安全。
        // 在依赖注入（DI）场景中，as 操作符常用于从 DI 容器中获取服务
        // as IUserRepository 尝试将 object 类型的返回值转换为 IUserRepository 类型。
        // var _userRepository =
        //     context.HttpContext.RequestServices.GetService(typeof(IUserRepository)) as IUserRepository;
        // var _rolePermissionRepository =
        //     context.HttpContext.RequestServices.GetService(typeof(IRolePermissionRepository)) as
        //         IRolePermissionRepository;
        // var _permissionRepository =
        //     context.HttpContext.RequestServices.GetService(typeof(IPermissionRepository)) as IPermissionRepository;
        var _userRepository = context.HttpContext.RequestServices.GetService<IUserService>();
        var _rolePermissionRepository = context.HttpContext.RequestServices.GetService<IRolePermissionService>();
        var _permissionRepository = context.HttpContext.RequestServices.GetService<IPermissionService>();
        var _auditLogService = context.HttpContext.RequestServices.GetService<IAuditLogService>();
        var _userManager = context.HttpContext.RequestServices.GetService<UserManager<EleUser>>();

        //角色为刷新令牌角色
        if (role != null && role.Equals(AppSettings.RefreshTokenRole))
        {
            //不允许访问其他接口
            if (!context.HttpContext.Request.Path.Value.Contains("/auth/refreshToken",
                    StringComparison.OrdinalIgnoreCase))
            {
                var result = new ForbidResult();
                context.Result = result;

                //在Asp.net Core优先执行AuthorizationFilter的，过滤器ActionFilterAttribute是在后面执行的，
                //所以我们要在权限过滤器这边，添加因为无权限返回的请求日志
                EleAuditLog auditLog = AuditLogHelper.GetRequestAuditLog(context, _userManager);
                auditLog.AuditLogType = AuditLogType.Exception;
                auditLog.ExceptionMessage = "无访问权限";
                auditLog.ReturnValue = JsonConvert.SerializeObject(result);
                _auditLogService.Add(auditLog);
            }

            return;
        }

        //检查请求的api是否在权限列表中有配置 如果没有配置，当前请求的API没有被特别地配置权限规则，则默认有权限
        // 2、获取所有API权限列表；
        var permissionList = await _permissionRepository.GetAll();
        // 3、检查当前请求的API是否在权限列表中被配置，并且是否匹配当前请求的方法和路径
        var existPermission = permissionList.FirstOrDefault(x =>
            context.HttpContext.Request.Method.ToLower().Equals(x.ApiMethod.ToLower()) && Regex.Match(context
                .HttpContext
                .Request.Path.Value.ToLower(), x.Url.ToLower()).Success);
        // 如果没有找到匹配的权限，则继续执行请求
        if (existPermission == null)
        {
            return;
        }

        // 4、获取分配给登录用户的API权限列表；
        // 获取用户的角色列表
        var userRoleList = await _userRepository.GetRoleList(userName);
        // 用于存储用户所有角色的权限
        var accountPermissions = new List<AccountPermissionsDto>();
        foreach (var roleName in userRoleList)
        {
            // 获取当前角色的权限列表
            var rolePermissions = await _rolePermissionRepository.GetRolePermissions(roleName);
            //合并角色重复的权限
            // foreach (var rolePermission in rolePermissions)
            // {
            //     if (!accountPermissions.Contains(rolePermission))
            //     {
            //         accountPermissions.Add(rolePermission);
            //     }
            // }
            // 使用 Distinct 去重
            // accountPermissions.AddRange(rolePermissions.Distinct());
            // 使用 HashSet 去重
            var distinctRolePermissions = new HashSet<AccountPermissionsDto>(rolePermissions);
            accountPermissions.AddRange(distinctRolePermissions);
        }

        //检查用户是否具有请求api的权限
        var hasPermissions = accountPermissions.FirstOrDefault(x =>
            context.HttpContext.Request.Method.ToLower().Equals(x.ApiMethod.ToLower()) &&
            Regex.Match(context.HttpContext.Request.Path.Value.ToLower(), x.Url.ToLower()).Success);
        // 5、判断请求的API，是否存在分配的API列表中；如果存在，直接返回，如果不存在，返回状态码403 Forbidden。
        if (hasPermissions == null)
        {
            var result = new ForbidResult();
            context.Result = result;

            //在Asp.net Core优先执行AuthorizationFilter的，过滤器ActionFilterAttribute是在后面执行的，
            //所以我们要在权限过滤器这边，添加因为无权限返回的请求日志
            EleAuditLog auditLog = AuditLogHelper.GetRequestAuditLog(context, _userManager);
            auditLog.AuditLogType = AuditLogType.Exception;
            auditLog.ExceptionMessage = "无访问权限";
            auditLog.ReturnValue = JsonConvert.SerializeObject(result);
            _auditLogService.Add(auditLog);
        }
    }
}